'use strict'

// <--    还原前    -->
var a = "Hello";a += "A",a+="S",a+="T!",b = "This is a test!";

/**
 * <--    特点    -->
 * 1. 不论是一个字符串在声明中被初始化，还是第一个赋值语句开始，紧跟着若干个追加赋值语句（或表达式），不能有其他语句
 * 2. 那么可以将追加赋值语句还原成一个字符串，更容易阅读
 * 
 * <--    解决办法    -->
 * 1. 以声明或赋值表达式为突破口
 * 2. 依次吃后面紧跟的追加赋值表达式
 * 
 * <--    冗余判断    -->
 * 一定要是连续的追加赋值；
 * 
 **/

// <--    还原后    -->
var a = "HelloAST!";
b = "This is a test!";

// <----------------->

const {parse} = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const t = require('@babel/types');
const generator = require('@babel/generator').default; 

let xcode = `var a = "Hello";a += "A",a+="S",a+="T!",b = "This is a test!";`;

const visitor = {
    VariableDeclarator:{
        enter: function(variableDeclarator){
            // 变量声明的时候必须有字符串初始化，不然追加赋值后就有'undefined'前缀
            if (!t.isStringLiteral(variableDeclarator.get('init').node)){
                return;
            }
            let varLV = variableDeclarator.get('id').node.name, varRV = variableDeclarator.get('init').node.value;
            console.log(varLV, varRV);
            
            let variableDeclaration = variableDeclarator.findParent(path => t.isVariableDeclaration(path.node));
            let allNextStmts = variableDeclaration.getAllNextSiblings();
            
            // 挨个遍历下一条语句，此处最好不用while(nextStmt),nextStmt=nextStmt.getNextSibling()的方式挨个遍历，
            // 因为在循环内部会吃掉赋值表达式，如果吃完，最后一个语句为空语句时，循环会出问题
            for (let nextStmt of allNextStmts){
                // 先将语句中所有的表达式收集起来
                let expressions;
                if (t.isSequenceExpression(nextStmt.get('expression').node)){
                    expressions = nextStmt.get('expression.expressions');
                }else{
                    expressions = [nextStmt.get('expression')];
                }

                // 遍历每个表达式，一旦碰到不能追加赋值的便退出
                for (let expression of expressions){
                    if (!t.isAssignmentExpression(expression.node, {operator: '+='})
                          || expression.node.left.name != varLV
                          || !t.isStringLiteral(expression.node.right)){
                              return;
                    }

                    let addi = expression.node.right.value;
                    varRV += addi;

                    // 声明语句吃掉追加赋值表达式
                    variableDeclarator.node.init.value = varRV;
                    expression.remove();
                }
            }
        },
    },

    AssignmentExpression:{
        enter: function(path){},
    },
}

let ast = parse(xcode);
traverse(ast, visitor);
let {code} = generator(ast);

console.log(code);
